/*
 * Copyright (C) 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Gets a row of values from state. Ignores count.
 */
function fetchStateRow(index) {
  var state = wave.getState();
  return {
    'label': state.get('state.' + index + '.label', 'state' + index),
    'color': state.get('state.' + index + '.color', '#000000'),
    'value': state.get('state.' + index + '.value', 'value' + index),
  };
}

/**
 * Updates a delta object with a stateRow object.
 * @param delta A delta object to update.
 * @param index The index at which to update the state row.
 * @param stateRow The row data to update the delta with.
 */
function updateDelta(delta, index, stateRow) {
  delta['state.' + index + '.label'] = stateRow.label;
  delta['state.' + index + '.color'] = stateRow.color;
  delta['state.' + index + '.value'] = stateRow.value;
}

/**
 * Puts a row of status into an index position. Does not modify count.
 * @param index Where to place values
 * @param stateRow Row of values to place.
 */
function putStateRow(index, stateRow) {
  var state = wave.getState();
  delta = {};
  updateDelta(delta, index, stateRow);
  state.submitDelta(delta);
}


/**
 * Deletes a row of state.
 */
function removeStateRow(index) {
  var state = wave.getState();
  var count = parseInt(state.get('count', 0));
  var delta = {};
  for (var i = index; i < count - 1; i++) {
    var row = fetchStateRow(i+1);
    updateDelta(delta, i, row);
  }
  delta['count'] = '' + (count - 1);
  state.submitDelta(delta);
}


/** 
 * Swaps two rows.
 * @param index The lower of the two row indices to swap.
 */
function swapRows(index) {
  var delta = {};
  updateDelta(delta, index, fetchStateRow(index + 1));
  updateDelta(delta, index + 1, fetchStateRow(index));
  wave.getState().submitDelta(delta);
}


/**
 * Initializes the color chooser dialog.
 */
function initColorChooser() {
  $('#colorChooser').dialog({
      autoOpen: false,
      modal: true,
      closeOnEscape: true,
      show: 'explode',
      hide: 'explode'
  });

  // When a swatch is clicked, update the state color, and close the dialog
  $('#colorChooser .swatch').click(function () {
      var chooser = $('#colorChooser');
      var color = $(this).css('background');
      wave.getState().submitValue('state.' + chooser.data('index') + '.color', color);
      chooser.dialog('close');
  });
}

/**
 * Initializes buttons on a newly created row.
 * @param tr The newly created row element (already jquery-wrapped).
 */
function initButtons(tr) {
  function withIcon(icon) {
    return {
        text: false,
        icons: {
          primary: 'ui-icon-' + icon,
        }
    };
  }

  tr.find('button.delete').button(withIcon('closethick')).click(
      function() {
        removeStateRow(getRowIndex(this));
      });
  tr.find('button.up').button(withIcon('circle-arrow-n')).click(
      function() {
        swapRows(getRowIndex(this) - 1);
      });
  tr.find('button.down').button(withIcon('circle-arrow-s')).click(
      function() {
        swapRows(getRowIndex(this));
      });
}


/**
 * Finds row index of the row containing the given element.
 * @param e An HTML element within a data row.
 * @return The zero based row index.
 */
function getRowIndex(e) {
  return $(e).closest('tr').data('index');
}


/** 
 * Updates a JQuery buttons enable/disable state. 
 *
 * @param element The JQuery wrapped element containing the button to enable/disable.
 * @param enabled 
 */
function setButtonEnabled(element, enabled) {
  var elementIsEnabled = !element.button('option', 'disabled');
  if (enabled != elementIsEnabled) {
    element.button('option', 'disabled', !enabled);
    element.button('refresh');
  }
}

/**
 * Called when everything has loaded.
 */
function onAllLoaded() {
  initColorChooser();

  // What to do when label changed
  $('td.label input').live('change', function () {
      wave.getState().submitValue('state.' + getRowIndex(this) + '.label', $(this).val());
  });

  // What to do when color swatch is clicked
  $('td.color .swatch').live('click', function () {
      $('#colorChooser').data('index', getRowIndex(this));
      $('#colorChooser').dialog('open');
  });

  // What to do when value changed
  $('td.value input').live('change', function () {
      wave.getState().submitValue('state.' + getRowIndex(this) + '.value', $(this).val());
  });

  // Add row button
  $('#addRow').button().click(function () {
      var state = wave.getState();
      var count = parseInt(state.get('count', 0));
      var delta = {};
      updateDelta(delta, count, {label: '', color: '#000', value: ''});
      delta['count'] = '' + (count + 1);
      state.submitDelta(delta);
  });

  // Custom values button
  $('#customValues').change(function () {
      wave.getState().submitValue('customValues', '' + $('#customValues').is(':checked'));
  });

  // Make installer button
  $('#makeInstaller').button().click(function() {
      wave.getState().submitValue('makeNewInstallerWave', 'true');
  });

}


/**
 * Wave mode update callback.
 */
function onModeUpdated() {
  // Does not behave differently in edit vs view modes.
  gadgets.window.adjustHeight();
}


/**
 * Wave state update callback.
 */
function onStateUpdated() {
  var state = wave.getState();
  var count = parseInt(state.get('count', 0));
  if (count == 0) {
    var delta = {};
    updateDelta(delta, 0, {label: 'Row 1', color: '#FF0000', value: 'row1'});
    updateDelta(delta, 1, {label: 'Row 2', color: '#0000FF', value: 'row2'});
    delta['count'] = '2';
    state.submitDelta(delta);
  }

  // Make rows in table match UI state
  var trs = $('.states tbody tr');
  for (var i = 0; i < count; i++) {
    var tr;
    if (i < trs.size()) { 
      tr = $(trs[i]);
    } else {
      tr = $('#rowTemplate tr').clone();
      tr.data('index', i);
      initButtons(tr);
      $('.states tbody').append(tr);
    }
    
    var stateRow = fetchStateRow(i);
    tr.find('.label input').val(stateRow.label);
    tr.find('.color .swatch').css('background', stateRow.color);
    tr.find('.value input').val(stateRow.value);

    // Enable / disable action buttons
    setButtonEnabled(tr.find('button.up'), i > 0);
    setButtonEnabled(tr.find('button.down'), i < (count - 1));
    setButtonEnabled(tr.find('button.delete'), count > 1);
  }


  // Delete extra rows
  for (var i = count; i < trs.size(); i++) {
    $(trs[i]).remove();
  }

  // Deal with specify values checkbox
  var customValues = state.get('customValues') == 'true';
  $('td.value').css('display', customValues ? 'table-cell' : 'none');

  // Deal with make installer button
  var outstandingInstaller = !!state.get('makeNewInstallerWave', 'false');
  setButtonEnabled($('#makeInstaller'), outstandingInstaller);
  
  gadgets.window.adjustHeight();
}



// 
// Boilerplate for initialisation
//
// Ensures JS libraries, mode and state are ready before continuing.
//
// Elsewhere is defined:
//  - onAllLoaded() - Called when gadget has all dependencies loaded, before
//     mode and state are registered.
//  - onModeUpdated() - called when mode has been updated.
//  - onStateUpdated() - called when state has been updated.
//
var libsLoaded = false;
var modeLoaded = false;
var stateLoaded = false;

function checkGadgetReady() {
  if (!libsLoaded || !modeLoaded || !stateLoaded) {
    return;
  }
  onAllLoaded();
  wave.setModeCallback(onModeUpdated);
  wave.setStateCallback(onStateUpdated);
}

gadgets.util.registerOnLoadHandler(function() {
    wave.setStateCallback(function() {
      stateLoaded = true;
      checkGadgetReady();
    });
    wave.setModeCallback(function() {
      modeLoaded = true;
      checkGadgetReady();
    });
});

google.setOnLoadCallback(function() {
  libsLoaded = true;
  checkGadgetReady();
});

